home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 11 - 1995 / 11.04 Apr 95 / Performance / Fractal 1 / FractalMain.c < prev   
Encoding:
C/C++ Source or Header  |  1995-01-22  |  15.7 KB  |  550 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:            FractalMain.c
  3.     
  4.     Used to build:    “Fractal 1”
  5.     
  6.     Written by:        Jim Cathey            July 1985
  7.                     Eric Traut            November 1994
  8.  
  9.     Description:
  10.         The following code implements a “Fractal Contour” generating
  11.         program. The program was originally written in Microsoft BASIC
  12.         and ported to Aztec C in 1985 by Jim Cathey. It has been a
  13.         widely-distributed public domain application since that time.
  14.  
  15.         The program generates a fractal surface by starting with a 
  16.         triangular surface. It then subdivides the triangle into four
  17.         subtriangles by calculating the midpoint of each edge. The 
  18.         Z coordinate for each of these edges is then randomly incremented
  19.         or decremented. This processes is repeated for each of the
  20.         subtrianges until a jagged fractal surface results.
  21.         
  22.         The original program uses a “cordic” function to approximate
  23.         the calculation of various transcendental functions used in
  24.         three-dimensional graphics transforms.
  25.         
  26.         In 1994, Eric Traut modified the code to run under System 7.5.
  27.         The program was also updated so it could be recompiled under 
  28.         PowerPC. This source is part of a series of modifications made 
  29.         to demonstrate code optimizations under PowerPC.
  30.         
  31.         ------------------------------------------------------------
  32.         
  33.         The following documents the changes made to the program at
  34.         each step of the optimization process and lists the speed
  35.         as measured under a PowerMac 8100/80av (8-bit 13"monitor).
  36.         All stages were compiled using Metrowerks’ CodeWarrier
  37.         1.2 compilers.
  38.         
  39.         
  40.         • STAGE 1:
  41.         0.59 fractals per second
  42.         
  43.         The first step to optimizing for PowerPC is to clean up 
  44.         the source code so it compiles under today’s 68K compilers and
  45.         runs under today’s system software. I also added a feature
  46.         to allow repeated calculation of fractals and a display
  47.         of “fractals per second.” This value will be used to measure
  48.         our progress as we further optimize the program. Note that
  49.         this version runs emulated on a PowerMac.
  50. */
  51.  
  52.  
  53. #include <Types.h>
  54. #include <Memory.h>
  55. #include <Quickdraw.h>
  56. #include <Fonts.h>
  57. #include <Windows.h>
  58. #include <OSUtils.h>
  59. #include <Menus.h>
  60. #include <Events.h>
  61. #include <TextEdit.h>
  62. #include <Dialogs.h>
  63. #include <Desk.h>
  64. #include <Controls.h>
  65. #include <ToolUtils.h>
  66. #include <Resources.h>
  67. #include <Strings.h>
  68.  
  69. #include <stdio.h>
  70.  
  71. #include "Fractal.h"
  72.  
  73. /* Functions defined within the file */
  74. void InitApp(void);
  75. void SetUpMenus(void);
  76. void SetUpWindow(void);
  77. void MainEventLoop(void);
  78. void UpdateWindow(WindowPtr theWindow);
  79. void DrawTimeInfo(void);
  80. Boolean DoMenuCommand(long mresult);
  81. void DoSetUpDialog(void);
  82. void SetTerrainButton(DialogPtr ptr, short oncontrl, short offcontrl);
  83. void DoAboutBox(void);
  84. void ReportFatalError(void);
  85.  
  86. /* Global variables */
  87. short         (*gPointArray)[kMaxXPoint][kMaxYPoint] = NULL;            
  88.                                         /* The array of points to be subdivided */
  89. short         gContourType;                /* Contour type */
  90. short         gContourLevel;                /* Level of detail */
  91. WindowPtr     gMainWindow;                /* Our one window */
  92. short        gMainWindowHeight;            /* Height of main window */
  93. short        gMainWindowWidth;            /* Width of main window */
  94. Boolean        gContinuousRedraw;            /* Continuously redraw fractal */
  95. long        gTotalTickCount;            /* Total time spent calculating and drawing */
  96. long        gTotalFractals;                /* Total fractal count */
  97. Boolean        gTimeUpdate;                /* Should this screen update be counted? */
  98.  
  99. /*
  100.     main
  101. */
  102. void main(void) 
  103. {
  104.     InitApp();
  105.     MainEventLoop();
  106. }
  107.  
  108.  
  109. /*
  110.     MainEventLoop
  111. */  
  112. void MainEventLoop(void)
  113. {
  114.     EventRecord         curEvent;            /* Event we should respond to */
  115.     WindowPtr             whichWindow;        /* Window which received the click */
  116.     Boolean             userDone;            /* Should we quit the program? */
  117.  
  118.     userDone = false;
  119.  
  120.     /* Get next event, and handle it appropriately, until user quits */
  121.     while (!userDone) {
  122.         if (WaitNextEvent(everyEvent, &curEvent, 1, NULL)) {
  123.             switch (curEvent.what) {
  124.             case mouseDown:
  125.                 switch (FindWindow(curEvent.where, &whichWindow)) {
  126.                 case inSysWindow:    /* handle the desk accessories */
  127.                     SystemClick(&curEvent, whichWindow);
  128.                     break;
  129.                 case inMenuBar:     /* handle the command */
  130.                     userDone = DoMenuCommand(MenuSelect(curEvent.where));
  131.                     break;
  132.                 case inDrag:         /* No Drag region, treat as content */
  133.                     {
  134.                         Rect        limitRect;
  135.         
  136.                         limitRect = qd.screenBits.bounds;
  137.                         InsetRect(&limitRect, 4, 4);
  138.                         DragWindow(gMainWindow, curEvent.where, &limitRect);
  139.                         break;
  140.                     }
  141.                 case inContent:        /* Activate window */
  142.                     if (whichWindow == gMainWindow)
  143.                         if (whichWindow != FrontWindow())
  144.                             SelectWindow(whichWindow);
  145.                     break;
  146.                 case inGrow:         /* No Grow Region */
  147.                 case inGoAway:         /* We don’t have a GoAway region */
  148.                             break;
  149.                 }
  150.                 break;
  151.  
  152.             case keyDown: 
  153.             case autoKey:            /* If command key, pass the char to MenuKey */
  154.                 if (curEvent.modifiers & cmdKey) 
  155.                     userDone = DoMenuCommand(MenuKey((char)(curEvent.message & charCodeMask)));
  156.                 break;
  157.             case updateEvt:            /* If it’s for our window, update it */
  158.                 if ((WindowPtr)curEvent.message == gMainWindow)
  159.                     UpdateWindow(gMainWindow);
  160.                     break;
  161.             case activateEvt:         /* If for our window, set port as necessary */
  162.                 if ((WindowPtr)curEvent.message == gMainWindow) {
  163.                     if (curEvent.modifiers & 1) {
  164.                                     /* odd means an activate event */
  165.                         SetPort(gMainWindow);
  166.                         DisableItem(GetMenu(kEditMenuID), 0);
  167.                         EnableItem(GetMenu(kFileMenuID), 0);
  168.                         EnableItem(GetMenu(kOptionsMenuID), 0);
  169.                         DrawMenuBar();
  170.                     }
  171.                     else {
  172.                         EnableItem(GetMenu(kEditMenuID), 0);
  173.                         DisableItem(GetMenu(kFileMenuID), 0);
  174.                         DisableItem(GetMenu(kOptionsMenuID), 0);
  175.                         DrawMenuBar();
  176.                     }
  177.                 }
  178.                 break;
  179.             }
  180.         }
  181.         else {
  182.             /* If there is no other event to handle (i.e. we got a null event)
  183.                 and we are in “continuous redraw” mode, we can calculate and display
  184.                 the next fractal. */
  185.             if (gContinuousRedraw && !gTimeUpdate) {
  186.                 CalcSurface(gContourLevel);
  187.                 InvalRect(&gMainWindow->portRect);
  188.             }
  189.         }    
  190.     }
  191. }
  192.  
  193.  
  194. /*
  195.     DoMenuCommand
  196.  
  197.    This function responds to the menu command returned by MenuSelect.
  198.    If it was Quit, we return true, else false.  Since the menu was
  199.    highlighted by MenuSelect, we must finish by unhighlighting it
  200.    to indicate we're done.
  201. */
  202. Boolean DoMenuCommand(long menuResult)
  203. {
  204.     short            menuID;                /* menu ID of selected menu */
  205.     short            itemNumber;            /* item number of selected menu item */
  206.     Boolean            quitSelected;        /* was menu item Quit? */
  207.  
  208.     quitSelected = false;                /* Assume Quit not selected */
  209.     menuID = HiWord(menuResult);        /* Get the menu selected */
  210.     itemNumber = LoWord(menuResult);    /* ... and the item of that menu */
  211.  
  212.     switch (menuID) {
  213.     case kAppleMenuID: 
  214.         if (itemNumber == kAboutBoxItem)    /* Tell about FracCont */
  215.             DoAboutBox();
  216.         else {                              /* Run a desk accessory */
  217.             Str255            menuName;
  218.             GrafPtr         savedPort;
  219.  
  220.             GetPort(&savedPort);            /* Preserve port */
  221.             GetMenuItemText(GetMenu(kAppleMenuID), itemNumber, menuName);
  222.             (void) OpenDeskAcc(menuName);    /* Run the desk accessory */
  223.             SetPort(savedPort);                /* Restore port */
  224.         }
  225.         break;
  226.  
  227.         case kFileMenuID: 
  228.             switch (itemNumber) {
  229.                 case kNewFractalItem:       /* New Surface */
  230.                         CalcSurface(gContourLevel);
  231.                         InvalRect(&gMainWindow -> portRect);
  232.                         break;
  233.                 case kQuitItem: 
  234.                     quitSelected = true;    /* Quit */
  235.                     break;
  236.             }
  237.             break;
  238.  
  239.         case kOptionsMenuID:
  240.             switch (itemNumber) {
  241.             case kSetupItem:
  242.                 DoSetUpDialog();
  243.                 break;
  244.             case kContinuousItem:
  245.                 SetItemMark(GetMenu(kOptionsMenuID), kContinuousItem, gContinuousRedraw ?
  246.                         noMark : checkMark);
  247.                 gContinuousRedraw = !gContinuousRedraw;
  248.                 break;
  249.             }
  250.         break;
  251.     }
  252.  
  253.     HiliteMenu(0);                            /* Turn off hilighting on the menu just used */
  254.     return quitSelected;
  255. }
  256.  
  257.  
  258. /*
  259.     SetTerrainButton
  260.     
  261.     This function is called by the DoSetUpDialog function to change
  262.     the current terrain button.
  263. */
  264. void SetTerrainButton(DialogPtr theDialog, short newControlID, short oldControlID)
  265. {
  266.     short                itemType;
  267.     ControlHandle        theControl;
  268.     Rect                itemBox;
  269.  
  270.     GetDialogItem(theDialog, oldControlID, &itemType, (Handle*)&theControl, &itemBox);
  271.     SetCtlValue(theControl, 0);     /* Turn off old default button */
  272.     GetDialogItem(theDialog, newControlID, &itemType, (Handle*)&theControl, &itemBox);
  273.     SetCtlValue(theControl, 1);     /* Turn on default button */
  274. }
  275.         
  276.  
  277. /*
  278.     DoSetUpDialog
  279.     
  280.     This function displays the set-up dialog and handles user actions
  281.     while the dialog is up.
  282. */
  283. void DoSetUpDialog(void)
  284. {
  285.     DialogPtr         theDialog;                /* Pointer to dialog */
  286.     long             newType;                /* Selected contour type */
  287.     long             newLevel;                /* Selected level of detail */
  288.     Boolean         doAnother;                /* True if we need to recalc */
  289.     short             itemHit = 0;            /* Item number of selected item */
  290.     Str255             itemText;                /* Used for converting string to num */
  291.     short             itemType;                /* Dummy variable needed for GetDialogItem */
  292.     Handle            itemHandle;                /* Item handle returned by GetDialogItem */
  293.     Rect            itemBox;                /* Item bound box returned by GetDialogItem */
  294.         
  295.     newType = gContourType;                    /* Initialize local vars to current values */
  296.     newLevel = gContourLevel;
  297.     doAnother = false;
  298.  
  299.     /* Display the dialog and set initial values if controls */
  300.     theDialog = GetNewDialog(kSetUpDialogID, NULL, (WindowPtr) -1);
  301.     SetTerrainButton(theDialog, gContourType, gContourType);
  302.     NumToString(newLevel, itemText);
  303.     GetDialogItem(theDialog, kSetUpLevelID, &itemType, &itemHandle, &itemBox);
  304.     SetDialogItemText(itemHandle, itemText);
  305.     SelIText(theDialog, kSetUpLevelID, 0, 32767);    /* Hilite entire text field. */
  306.     
  307.     while (itemHit != kSetUpOKButtonID && itemHit != kSetUpCancelButtonID) {
  308.         ModalDialog(NULL, &itemHit);
  309.         GetDItem(theDialog, kSetUpLevelID, &itemType, &itemHandle, &itemBox);
  310.         GetIText(itemHandle, itemText);                /* Get Level field */
  311.         StringToNum(itemText, &newLevel);
  312.         
  313.         /* Check for legal contour level */
  314.         if (newLevel < 1 || newLevel > kMaxLevel) {
  315.             SysBeep(0);
  316.             newLevel = gContourLevel;
  317.             NumToString(newLevel, itemText);
  318.             GetDialogItem(theDialog, kSetUpLevelID, &itemType, &itemHandle, &itemBox);
  319.             SetDialogItemText(itemHandle, itemText);
  320.             SelIText(theDialog, kSetUpLevelID, 0, 100);  /* Hilite text field. */
  321.         }
  322.  
  323.         if (itemHit == kSetUpMtnButtonID || 
  324.                 itemHit == kSetUpHillsButtonID ||
  325.                 itemHit == kSetUpWaterButtonID) {
  326.             SetTerrainButton(theDialog, itemHit, newType);
  327.             newType = itemHit;
  328.         }
  329.  
  330.         if (itemHit == kSetUpOKButtonID && ((newLevel > 0) & (newLevel <= kMaxLevel))) {
  331.             if (gContourType != newType || gContourLevel != newLevel) {
  332.                 doAnother = true;
  333.                 InvalRect(&gMainWindow->portRect);
  334.                 gContourLevel = newLevel;
  335.                 gContourType = newType;
  336.             }
  337.         }
  338.     }
  339.     
  340.     DisposeDialog(theDialog);        /* Release storage and remove dialog from screen */
  341.  
  342.     if (doAnother) {
  343.         gTotalTickCount = 0;
  344.         gTotalFractals = 0;
  345.         CalcSurface(newLevel);
  346.     }
  347. }
  348.  
  349.  
  350. /*
  351.     DoAboutBox
  352.     
  353.     This function displays the about box dialog.
  354. */
  355. void DoAboutBox(void)
  356. {
  357.     short            itemHit;            
  358.     DialogPtr        theDialog;
  359.  
  360.     theDialog = GetNewDialog(kAboutBox1DialogID, NULL, (WindowPtr) -1);
  361.     ModalDialog(NULL, &itemHit);
  362.     DisposeDialog(theDialog);
  363.  
  364.     if (itemHit == kAboutBoxMoreButtonID) {
  365.         theDialog = GetNewDialog(kAboutBox2DialogID, NULL, (WindowPtr) -1);
  366.         ModalDialog(NULL, &itemHit);
  367.         DisposeDialog(theDialog);
  368.     }
  369. }
  370.  
  371.  
  372. /*
  373.     SetUpMenus
  374.     
  375.     This function initilizes the menu bar and the applications menus.
  376. */
  377. void SetUpMenus(void)
  378.  {
  379.     SetMenuBar(GetNewMBar(kMenuBarID));
  380.     AddResMenu(GetMenu(kAppleMenuID), 'DRVR');
  381.     DrawMenuBar();
  382. }
  383.  
  384.  
  385. /*
  386.     SetUpWindow
  387.     
  388.     This function initializes the main window and its associated
  389.     global variables.
  390. */
  391. void SetUpWindow(void)
  392. {
  393.     GDHandle            mainDeviceHandle;
  394.     short                maxWindowHeight, maxWindowWidth;
  395.  
  396.     gMainWindow = GetNewCWindow(kMainWindowID, NULL, (WindowPtr)-1);
  397.      mainDeviceHandle = GetMainDevice();
  398.      
  399.     maxWindowHeight = (*mainDeviceHandle)->gdRect.bottom - (*mainDeviceHandle)->gdRect.top - 
  400.              (2 * kScreenBoundaryBits) - GetMBarHeight() - kWindowTitleHeight;
  401.      maxWindowWidth = (*mainDeviceHandle)->gdRect.right - (*mainDeviceHandle)->gdRect.left - 
  402.              (2 * kScreenBoundaryBits);
  403.      
  404.      if (maxWindowHeight >= kNewScreenY && maxWindowWidth >= kNewScreenX) {
  405.          gMainWindowHeight = kNewScreenY;
  406.          gMainWindowWidth = kNewScreenX;
  407.      }
  408.      else {
  409.          gMainWindowHeight = maxWindowHeight;
  410.          gMainWindowWidth = maxWindowWidth;
  411.      }
  412.      
  413.      SizeWindow(gMainWindow, gMainWindowWidth, gMainWindowHeight, false);
  414.      MoveWindow(gMainWindow,
  415.              (*mainDeviceHandle)->gdRect.left + kScreenBoundaryBits,
  416.              (*mainDeviceHandle)->gdRect.top + kScreenBoundaryBits + GetMBarHeight() + kWindowTitleHeight,
  417.              false);
  418.      ShowWindow(gMainWindow);
  419. }
  420.  
  421.  
  422. /*
  423.     InitApp
  424.     
  425.     This function initializes the program the Mac toolbox.
  426. */
  427. void InitApp(void)
  428. {
  429.     InitGraf(&qd.thePort);
  430.     InitFonts();
  431.     InitWindows();
  432.     InitMenus();
  433.     TEInit();
  434.     InitDialogs(NULL);
  435.     InitCursor();
  436.  
  437.      gContinuousRedraw = false;
  438.     gTotalTickCount = 0;                    /* Initialize performance counters */
  439.     gTotalFractals = 0;
  440.     
  441.     gContourType = kDefaultStyle;            /* Default style */
  442.     gContourLevel = kDefaultLevel;            /* Default Level */
  443.  
  444.      SetUpWindow();                            /* Create new window */
  445.     SetUpMenus();                            /* Set up our menus */
  446.  
  447.     /* Allocate the data array. */
  448.     gPointArray = (void*)NewPtr((long) kMaxXPoint*kMaxYPoint*(sizeof(short)));
  449.  
  450.     /* Make sure we got some memory */
  451.     if (gPointArray == NULL)
  452.         ReportFatalError();
  453.  
  454.     CalcSurface(gContourLevel);             /* Do at least one first */
  455. }
  456.  
  457.  
  458. /*
  459.     UpdateWindow
  460.  
  461.     This is our response to receipt of an update event for 
  462.     gMainWindow. It redraws our main window, respecting the
  463.     current clip region, etc.
  464. */
  465.  
  466. void UpdateWindow(WindowPtr theWindow)
  467. {
  468.     GrafPtr                savedPort;            /* Used to temporarily save cur port */
  469.      CursHandle            clockCursor;        /* Used to display busy cursor */
  470.     Rect                windowRect;            /* Rect of entire window */
  471.     long                startTicks;            /* Used for performance timing */
  472.  
  473.     clockCursor = GetCursor(watchCursor);
  474.     if (clockCursor)
  475.         SetCursor(*clockCursor);       /* Show busy cursor */
  476.     
  477.     GetPort(&savedPort);            /* Save current port */
  478.     SetPort(theWindow);                /* Work in the specified window */
  479.     BeginUpdate(theWindow);
  480.     
  481.     startTicks = TickCount();        /* Record current tick count */
  482.     
  483.     /* Erase portion of window that may change */
  484.     SetRect(&windowRect, 0, 0, gMainWindowWidth, gMainWindowHeight - 20);
  485.     EraseRect(&windowRect);
  486.     SetRect(&windowRect, 200, gMainWindowHeight - 20, gMainWindowWidth, gMainWindowHeight);
  487.     EraseRect(&windowRect);
  488.     PlotData();                        /* Redraw contents of window */
  489.     
  490.     /* If this is an update in response to a recomputation, we will count it
  491.         as a part of the total time for the current fractal. If the update is
  492.         in response to something else (e.g. the window coming to the front),
  493.         we won’t time it. */
  494.     if (gTimeUpdate) {
  495.         gTimeUpdate = false;
  496.         gTotalTickCount += TickCount() - startTicks;
  497.     }
  498.     
  499.     DrawTimeInfo();                    /* Draw performance statistics */
  500.     
  501.     SetPort(savedPort);                /* Restore previous port */
  502.     EndUpdate(theWindow);
  503.     InitCursor();                    /* Go back to arrow cursor */
  504. }
  505.  
  506.  
  507. /*
  508.     DrawTimeInfo
  509. */
  510. void DrawTimeInfo(void)
  511. {
  512.     Str255        tempString;            /* Used for creating statistics string */
  513.     Rect        tempRect;            /* Rect to erase */
  514.  
  515.     TextFont(geneva);
  516.     TextSize(12);
  517.     MoveTo(20, gMainWindowHeight - 5);
  518.     DrawString("\pFractals per second: ");
  519.  
  520.     if (gTotalTickCount != 0) {        /* Make sure we don’t divide by zero */
  521.         SetRect(&tempRect, 150, gMainWindowHeight - 20, 200, gMainWindowHeight);
  522.         EraseRect(&tempRect);
  523.         sprintf((char*)tempString, "%3.3f", 
  524.                 (double)gTotalFractals * 60 / (double)gTotalTickCount);
  525.         c2pstr((char*)tempString);
  526.         DrawString(tempString);
  527.     }
  528. }
  529.  
  530.  
  531.  
  532. /*
  533.     ReportFatalError
  534.     
  535.     This function displays a fatal error alert and terminates the program.
  536. */
  537. void ReportFatalError(void)
  538. {
  539.     (void) Alert(kFatalErrorAlertID, NULL);
  540.     ExitToShell();
  541. }
  542.  
  543.  
  544.  
  545.  
  546.  
  547.  
  548.  
  549.  
  550.